/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * \file
 * \brief Thread toolbox class.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * \todo Should the StaticThreadFuncRunner function mark the thread as
 * 'not-running' when it exits? Or is it OK to just let the WaitThread
 * function to do it?
 */
#ifndef _GIM_UTILS_THREAD_HPP_
#define _GIM_UTILS_THREAD_HPP_
#include "ownutils.h"
#include "owndebug.h"
#include <utility>
#include <vector>
namespace gim {

  
  /** Threading abstraction baseclass.
   *
   * This class imlements a interface intended for simple creation
   * of threads in C++ program. 
   */
  class CThread
  {
  private:
    struct TThreadEntry {
      TThreadEntry(ownThreadHandle h = ownThreadHandleNullInitializer, 
                   bool running = false,
                   const int cS = 0, 
                   const int cT = 0) 
        : threadFunctionHandle(h),
          threadWaited(false),
          threadFunctionRunning(running),
          requestThreadTermination(false),
          cancelState(cS),
          cancelType(cT) {}
      ownThreadHandle threadFunctionHandle;
      volatile bool threadWaited;
      volatile bool threadFunctionRunning;
      volatile bool requestThreadTermination;
      int cancelState;
      int cancelType;
    };
    
  public:

    /** Constructor.
     *
     * Thread class constructor takes the maximum number of threads
     * to run as a parameter.
     *
     * @param[in] aThreadsMax   Maximum number of threads to run.
     */
    CThread(const unsigned int aThreadsMax = 1)
      : iThreadTable(aThreadsMax, TThreadEntry()),
        iThreadTableMutex(ownMutex_Init()) {
      dPrint(8,"CThread %p constructed", this);
    }

    virtual ~CThread() {
      for (unsigned int i = 0; i < iThreadTable.size(); ++i) {
        if (IsThreadRunning(i)) {
          // Set user-level Request-Termination
          SetRequestTermination(i);
          // Attempt cancel.
          CancelThread(i);
          // Wait until thread terminates.
          WaitThread(i);
        }
      }
      ownMutex_Lock(iThreadTableMutex);
      ownMutex_Unlock(iThreadTableMutex);
      ownMutex_Destroy(iThreadTableMutex);
      iThreadTableMutex = NULL;
      dPrint(8,"CThread %p destructed", this);
    }
    
    CThread(const CThread &t) 
      : iThreadTable(t.iThreadTable.size(), TThreadEntry()),
        iThreadTableMutex() {
      abort();
    }
  
    
    CThread &operator=(const CThread &) { 
      abort();
      return *this; 
    }
    

    /** Start a thread.
     *
     * This function starts a new executing thread with given thread number.
     * The given thread number must be smaller than maximum number of threads
     * specified in the class constructor.
     * 
     * @param[in] aThreadNumber Number of thread to start. This number is
     *                          also passed as a parameter to the thread 
     *                          function.
     * @param[in] aCancelState  Define the cancel state for this thread.
     *                          Cancel state can be 'ownThreadCancelStateEnable'
     *                          or 'ownThreadCancelStateDisable'.
     * @param[in] aCancelType   Define the cancel type for this thread. Cancel
     *                          type can be 'ownThreadCancelTypeDeferred' or
     *                          'ownThreadCancelTypeAsync'. Deferred will
     *                          wait first available 'Cancellation point' after
     *                          getting the cancellation request, while the
     *                          ASync will terminate immediately.
     * @return                  'true' when the thread was succesfully started.
     *                          'false' when the thread number is out of bounds.
     */
    bool RunThread(const unsigned int aThreadNumber = 0,
                   const int aCancelState = ownThreadCancelStateDisable, 
                   const int aCancelType = ownThreadCancelTypeDeferred) {


      bool result = false;
      ownMutex_Lock(iThreadTableMutex);
      
      // Check that requested thread is inside table
      if (aThreadNumber < iThreadTable.size()) {
        if (!iThreadTable[aThreadNumber].threadFunctionRunning) {
          iThreadTable[aThreadNumber].threadFunctionRunning = true;
          iThreadTable[aThreadNumber].threadWaited = false;
          iThreadTable[aThreadNumber].cancelState = aCancelState;
          iThreadTable[aThreadNumber].cancelType = aCancelType;          
          iThreadTable[aThreadNumber].threadFunctionHandle = ownThread_Create(reinterpret_cast<void*>(StaticThreadFuncRunner),
                                                                              new std::pair<CThread *, int>(this, int(aThreadNumber)));
          
          dPrint(8,"Created thread %p with number %d",
                 iThreadTable[aThreadNumber].threadFunctionHandle, aThreadNumber);
          result = true;
        } else {
          dPrint(3,"Failed to create thread");
          
        }
      }
      ownMutex_Unlock(iThreadTableMutex);
      return result;
    }


    /** Send signal to thread.
     * 
     * This function sends the specified signal to the given thread.
     *
     * @param[in] aThreadNumber Number of thread to signal
     * @param[in] aSignal Signal to send (Default is SIGTERM)
     * @return                  'true' when the signal was succesfully sent.
     *                          'false' when the thread number is out of bounds.
     *
     */
    int SignalThread(const unsigned int aThreadNumber = 0, int aSignal = SIGTERM)
    {
      int result = -1;
      
      ownMutex_Lock(iThreadTableMutex);
      // Check that requested thread is inside table
      if (aThreadNumber < iThreadTable.size() &&
          iThreadTable[aThreadNumber].threadFunctionRunning) {
        dPrint(8,"Signaling thread %p with number %d",
               iThreadTable[aThreadNumber].threadFunctionHandle, aThreadNumber);

        ownThread_Signal(iThreadTable[aThreadNumber].threadFunctionHandle, aSignal);
	
        dPrint(8,"Signaling completed to thread %p with number %d",
               iThreadTable[aThreadNumber].threadFunctionHandle, aThreadNumber);

        iThreadTable[aThreadNumber].threadFunctionHandle = ownThreadHandleNullInitializer;
        iThreadTable[aThreadNumber].threadFunctionRunning = false;

      }
      ownMutex_Unlock(iThreadTableMutex);
      return result;
    }


    /** Wait for thread to exit.
     *
     * This function waits until the Thread has completed. It will wait forever,
     * so be sure you have signaled the thread in some way to exit.
     *
     * @param[in] aThreadNumber Number of thread to wait.
     * @return                  Return value from the thread, or -1 on error.
     *                          (Note that the thread might also return -1)
     */
    int WaitThread(const unsigned int aThreadNumber = 0) {

      int result = -1;

      ownMutex_Lock(iThreadTableMutex);

      // Check that requested thread is inside table
      if (aThreadNumber < iThreadTable.size() &&
          iThreadTable[aThreadNumber].threadFunctionRunning &&
          iThreadTable[aThreadNumber].threadWaited == false) {

        // Update Waited flag (No more waits allowed!)
        iThreadTable[aThreadNumber].threadWaited = true;
        
        dPrint(8,"Joining thread %p with number %d",
               iThreadTable[aThreadNumber].threadFunctionHandle, aThreadNumber);

        // Because now the entry is marked as "waited", we can release
        // these locks.
        ownMutex_Unlock(iThreadTableMutex);
        ownThread_Join(iThreadTable[aThreadNumber].threadFunctionHandle, &result);
        ownMutex_Lock(iThreadTableMutex);

        dPrint(8,"Join completed to thread %p with number %d",
               iThreadTable[aThreadNumber].threadFunctionHandle, aThreadNumber);
        
        iThreadTable[aThreadNumber].threadFunctionHandle = ownThreadHandleNullInitializer;
        iThreadTable[aThreadNumber].threadFunctionRunning = false;
      }/* else {
        dPrint(1,"FAILED TO WAIT THREAD! (aThreadNumber %d, iThreadTable.size() %d, "
               "iThreadTable[aThreadNumber].threadFunctionRunning %d, "
               "iThreadTable[aThreadNumber].threadWaited %d)", 
               aThreadNumber, iThreadTable.size(), iThreadTable[aThreadNumber].threadFunctionRunning,
               iThreadTable[aThreadNumber].threadWaited);
        
               }*/
      ownMutex_Unlock(iThreadTableMutex);
      return result;
    }
    
    
    /** Cancel a running thread.
     *
     * This functions attempts to cancel a running thread. 
     *
     * @param[in] aThreadNumber Number of thread to Cancel.
     * @return                  'true' when Cancel was succesfully
     *                          called for this thread. <b> Note:
     *                          the thread may not have returned when 
     *                          this function returns </b>
     *
     * \todo The documentation should include more details about the
     *       nature of this function.
     */
    bool CancelThread(const unsigned int aThreadNumber = 0) {
      bool result = false;
      ownMutex_Lock(iThreadTableMutex);
      ownMutex_Lock(iThreadTableMutex);
      // Check that requested thread is inside table
      if (aThreadNumber < iThreadTable.size() &&
          iThreadTable[aThreadNumber].threadFunctionRunning) {
        dPrint(8,"Canceling thread %p with number %d",
               iThreadTable[aThreadNumber].threadFunctionHandle, aThreadNumber);
        
        ownThread_Cancel(iThreadTable[aThreadNumber].threadFunctionHandle);
        
        dPrint(8,"Canceling completed on thread %p with number %d",
               iThreadTable[aThreadNumber].threadFunctionHandle, aThreadNumber);
      }
      ownMutex_Unlock(iThreadTableMutex);
      return result;
    }
    

    /** Test whether a Thread is still running.
     * 
     * This function returns 'true' when the requested thread is still
     * marked as Running (It has not been succesfully Waited for)
     *
     * @param[in] aThreadNumber Number of thread to check.
     * @return                  'true' when the thread is still marked as running,
     *                          'false' when it is not.
     */
    bool IsThreadRunning(const unsigned int aThreadNumber = 0) const {
      bool result = false;
      // Check that requested thread is inside table
      ownMutex_Lock(iThreadTableMutex);
      if (aThreadNumber < iThreadTable.size()) {
        result = iThreadTable[aThreadNumber].threadFunctionRunning;
      }
      ownMutex_Unlock(iThreadTableMutex);
      return result;
    }
    
    
    /**
     * Checks whether flag 'RequestTermination' is set for corresponding thread.
     * 
     * This function returns the state of flag
     * RequestTermination. This flag is purely user-level parameter,
     * and has no effect on the thread handling routine.  It can be
     * used on application level to control thread flow through common
     * mechanism.
     * 
     * @param[in] aThreadNumber Number of thread to check.
     * @return                  'true' when the thread is marked for RequestTermination,
     *                          'false' when it is not.
     */
    bool IsRequestTermination(const unsigned int aThreadNumber = 0) const {
      bool result = false;
      // Check that requested thread is inside table      
      ownMutex_Lock(iThreadTableMutex);
      if (aThreadNumber < iThreadTable.size()) {
        result = iThreadTable[aThreadNumber].requestThreadTermination;
      }
      ownMutex_Unlock(iThreadTableMutex);
      return result;
    }
    

    /**
     * Sets flag 'RequestTermination' for corresponding thread.
     * 
     * This function sets the state of flag RequestTermination. 
     *
     * \note This flag is purely user-level parameter, and has no
     * effect on the thread handling routine.  It can be used on
     * application level to control thread flow through common
     * mechanism.
     * 
     * @param[in] aThreadNumber Number of thread to set state to.
     * @param[in] aRequestState Request state to set. 'true' for SET,
     *                          'false' for unset.
     */
    void SetRequestTermination(const unsigned int aThreadNumber = 0, bool aRequestState = true) {
      // Check that requested thread is inside table
      ownMutex_Lock(iThreadTableMutex);
      if (aThreadNumber < iThreadTable.size()) {
        iThreadTable[aThreadNumber].requestThreadTermination = aRequestState;
      }
      ownMutex_Unlock(iThreadTableMutex);
    }


    /** Call ownThread_TestCancel on the current thread.
     *
     * This function sets a user-defined cancellation point for
     * deferred thread cancellation. You can call it always when you
     * want to give a change for the thread to stop for cancellation
     * request.
     */
    void TestCancel(void) {
      ownThread_TestCancel();
    }


    /** Set CancelState for _current_ thread.
     * \note This is experimental!
     */
    void SetCurrentThreadCancelState(const int aCancelState) const {
      ownThread_SetCancelState(aCancelState);
    }
    

    /** Set CancelType for _current_ thread.
     * \note This is experimental!
     */
    void SetCurrentThreadCancelType(const int aCancelType) const {
      ownThread_SetCancelType(aCancelType);
    }

    
    /** Thread running function.
     *
     * This is the function that derived classes must implement to use
     * this Threading class. When RunThread is called, this function is executed
     * once (its up to user when it return). The thread number is passed
     * to the function as parameter, so users can select the behaviour of 
     * the thread based on that value.
     * 
     * @param[in] aThreadNumber Identifier of the runner of this thread.
     * @return                  Return value is the value returned
     *                          by the WaitThread() call when the thread
     *                          has finished.
     */
    virtual int ThreadFunction(const int aThreadNumber) = 0;
    

  private:
    /** Wrapper function to take care of starting the member function
     * 'ThreadFunction'.
     */
    static int StaticThreadFuncRunner(std::pair<CThread *, int> *e) {
      // Take parameters and delete the temporary data
      CThread *that = e->first;
      const int number = e->second;
      delete e;

      // Set thread handling parameters
      ownThread_SetCancelState(that->iThreadTable[number].cancelState);
      ownThread_SetCancelType(that->iThreadTable[number].cancelType);

      dPrint(8,"Executing 'ThreadFunction(%d)'", number);

      // Call the user-implemented threadfunction.
      int rval = that->ThreadFunction(number);

      // Returned from userfunction
      dPrint(8,"Execution of 'ThreadFunction(%d)' ended.", number);

      // return
      return rval;
    }


  private:
    std::vector<TThreadEntry> iThreadTable; ///< Array of thread running entries.
    ownMutexHandle iThreadTableMutex;
  };
}

#endif
